home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Programming Sound Cards
/
Programming Sound Cards.iso
/
sound_35
/
gus.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-01-01
|
16KB
|
776 lines
/*
GUS.C
Michael Chen
mchen@cs.psu.edu
4/18/1993
See the included .TXT file for terms and other information.
Note that when playing back 16-bit samples, the start and end positions
are measured in 16-bit words, not bytes!
*/
#include "gus.h"
#include <stdio.h>
#include <dos.h>
int GUSBase;
int GUSMixer;
int GUSStatus;
int GUSTimerControl;
int GUSTimerData;
int GUSIRQDMAControl;
int GUSMIDIControl;
int GUSMIDIData;
int GUSVoice;
int GUSCommand;
int GUSDataLo;
int GUSDataHi;
int GUSDRAMIO;
int GUSCurrentVoice = -1;
int GUSVoices = MINVOICES;
byte GUSFreqDivisorTable[MAXVOICES+1] =
/*
Divisor table when setting voice frequency, based on number of voices.
Remember, minimum of 14 active voices (though divisors given for 8 and up).
*/
{ -1, -1, -1, -1, -1, -1, -1, -1, 74, 66, 60, 54, 50, 46, 43, 40, 37,
35, 33, 31, 30, 28, 27, 26, 25, 24, 23, 22, 21, 20, 20, 19, 18 };
void GUSDelay()
/*
Waits for GUS. (from Ultradox 2.0)
*/
{
int i;
for (i=0; i<7; i++)
inportb(0x300);
}
void GUSReset()
/*
Resets GUS. (from Ultradox 2.0)
*/
{
int i;
/* Force routines to select voice explicitly first time */
GUSCurrentVoice = -1;
/* Put GUS in initialization mode */
GUS_SetCommand(G_Initialize);
outportb(GUSDataHi,0);
GUSDelay();
GUSDelay();
/* Take GUS out of initialization mode */
GUS_SetCommand(G_Initialize);
outportb(GUSDataHi,1);
GUSDelay();
GUSDelay();
/* Ramp voices to 0 --- doesn't work yet */
/*
GUSShutUp();
*/
/* Clear DMA control */
GUS_SetCommand(G_DMAControl);
outportb(GUSDataHi,0);
/* Clear timer control */
GUS_SetCommand(G_TimerControl);
outportb(GUSDataHi,0);
/* Clear sample control */
GUS_SetCommand(G_SampleControl);
outportb(GUSDataHi,0);
/* Set number of voices to minimum. */
GUSSetVoices(MINVOICES);
/* Clear pending DMA control */
GUS_SetCommand(G_DMAControl);
inportb(GUSDataHi);
/* Clear pending timer control */
GUS_SetCommand(G_TimerControl);
inportb(GUSDataHi);
/* Clear pending sample control */
GUS_SetCommand(G_SampleControl);
inportb(GUSDataHi);
/* Reset voices */
for (i=0; i<MAXVOICES; i++)
{
/* Select voice */
GUSSelectVoice(i);
/* Stop voice */
GUSStopVoice(i);
/* Turn off volume ramp */
GUSStopRamp(i);
/* Set to zero volume */
GUSSetVolume(i,0);
/* Set to center pan */
GUSSetVoiceBalance(0,P_Center);
}
/* Clear pending DMA control */
GUS_SetCommand(G_DMAControl);
inportb(GUSDataHi);
/* Clear pending sample control */
GUS_SetCommand(G_SampleControl);
inportb(GUSDataHi);
/* Unknown read from IRQ status (?) */
GUS_SetCommand(G_IRQStatus);
inportb(GUSDataHi);
/* Do something to initialization register (?) */
GUS_SetCommand(G_Initialize);
outportb(GUSDataHi,7);
/* Set mixer to default (line-in and output on, mic-in off) */
GUSSetMixer(M_OutputOn|M_LineInOn|M_MicInOff);
}
byte GUSPeek(longword addr)
/*
Peeks at value from GUS DRAM (long address).
*/
{
return GUS_Peek(addr >> 16,addr & 0xFFFF);
}
void GUSPoke(longword addr, byte val)
/*
Pokes value to GUS DRAM (long address).
*/
{
GUS_Poke(addr >> 16,addr & 0xFFFF,val);
}
byte GUSPokePeek(longword addr, byte val)
/*
Pokes value to GUS DRAM (long address), then immediately peeks same.
*/
{
return GUS_PokePeek(addr >> 16,addr & 0xFFFF,val);
}
static byte GUS_Peek(byte addrhi, word addrlo)
/*
Peeks at value from GUS DRAM.
*/
{
GUS_SetCommand(G_SetDRAMLo);
outport(GUSDataLo,addrlo);
GUS_SetCommand(G_SetDRAMHi);
outportb(GUSDataHi,addrhi);
return GUS_ReadDRAMIO();
}
static void GUS_Poke(byte addrhi, word addrlo, byte val)
/*
Pokes value to GUS DRAM.
*/
{
GUS_SetCommand(G_SetDRAMLo);
outport(GUSDataLo,addrlo);
GUS_SetCommand(G_SetDRAMHi);
outportb(GUSDataHi,addrhi);
GUS_SetDRAMIO(val);
}
static byte GUS_PokePeek(byte addrhi, word addrlo, byte val)
/*
Pokes value to GUS DRAM, then immediately peek from same location.
*/
{
GUS_SetCommand(G_SetDRAMLo);
outport(GUSDataLo,addrlo);
GUS_SetCommand(G_SetDRAMHi);
outportb(GUSDataHi,addrhi);
outportb(GUSDRAMIO,val);
return GUS_ReadDRAMIO();
}
int ProbeGUS()
/*
Probes to see if GUS exists at current base address. (from Ultradox 2.0)
*/
{
byte testbyte = 0xAA;
byte test1;
GUS_SetCommand(G_Initialize);
outportb(GUSDataHi,0);
GUSDelay();
GUSDelay();
GUS_SetCommand(G_Initialize);
outportb(GUSDataHi,1);
GUSDelay();
GUSDelay();
GUSPoke(0,testbyte);
GUSPoke(1,0xFF - testbyte);
test1 = GUSPeek(0);
GUS_SetCommand(G_Initialize);
outportb(GUSDataHi,0);
return (test1 == testbyte);
}
int DetectGUS()
/*
Returns base address of GUS ports, or 0 if no GUS detected.
*/
{
for (GUSBase = 0x210; (GUSBase < 0x270); GUSBase += 0x10)
{
GUSMixer = GUSBase;
GUSStatus = GUSBase + 6;
GUSTimerControl = GUSBase + 8;
GUSTimerData = GUSBase + 9;
GUSIRQDMAControl = GUSBase + 0xB;
GUSMIDIControl = GUSBase + 0x100;
GUSMIDIData = GUSBase + 0x101;
GUSVoice = GUSBase + 0x102;
GUSCommand = GUSBase + 0x103;
GUSDataLo = GUSBase + 0x104;
GUSDataHi = GUSBase + 0x105;
GUSDRAMIO = GUSBase + 0x107;
if (ProbeGUS()) break;
}
if (GUSBase < 0x270)
return GUSBase;
else
return 0;
}
static void GUS_SelectVoice(byte voice)
/*
Selects current GUS voice for subsequent commands.
*/
{
GUS_SetVoice(voice);
GUSCurrentVoice = voice;
}
void GUSSelectVoice(byte voice)
/*
Changes current GUS voice for subsequent commands if necessary.
*/
{
if (GUSCurrentVoice != voice)
GUS_SelectVoice(voice);
}
void GUSStopVoice(byte voice)
/*
Stops GUS voice.
*/
{
byte b;
GUSSelectVoice(voice);
b = GUSReadVoiceMode(voice);
if (!(b & V_VoiceStopped))
{
b |= 3;
GUS_SetCommand(G_SetVoiceMode);
outportb(GUSDataHi,b);
}
}
void GUSStartVoice(byte voice)
/*
Starts GUS voice.
*/
{
byte b;
GUSSelectVoice(voice);
b = GUSReadVoiceMode(voice);
if (b & V_VoiceStopped)
{
b &= 0xFC;
GUS_SetCommand(G_SetVoiceMode);
outportb(GUSDataHi,b);
}
}
void GUSSetVoiceMode(byte voice, byte mode)
/*
Sets GUS voice mode (not start/stop, though) directly.
*/
{
byte b;
GUSSelectVoice(voice);
GUS_SetCommand(G_ReadVoiceMode);
b = inportb(GUSDataHi);
b &= 3;
b |= (mode & 0xFC);
GUS_SetCommand(G_SetVoiceMode);
outportb(GUSDataHi,b);
}
void GUSSetVoices(byte voices)
/*
Sets number of voices for GUS.
*/
{
GUS_SetCommand(G_SetMaxVoice);
outportb(GUSDataHi,((voices-1) | G_VoiceMask));
GUSVoices = voices;
}
void GUSSetVoiceFreq(byte voice, word freq)
/*
Sets GUS voice frequency, taking into account number of voices.
*/
{
GUSSelectVoice(voice);
GUS_SetCommand(G_SetVoiceFreq);
outport(GUSDataLo,freq/GUSFreqDivisorTable[GUSVoices]);
}
static void GUS_SetLoopStart(byte voice, word hi, word lo)
{
GUSSelectVoice(voice);
GUS_SetCommand(G_SetLoopStartLo);
outport(GUSDataLo,lo);
GUS_SetCommand(G_SetLoopStartHi);
outport(GUSDataLo,hi);
}
static void GUS_SetLoopEnd(byte voice, word hi, word lo)
{
GUSSelectVoice(voice);
GUS_SetCommand(G_SetLoopEndLo);
outport(GUSDataLo,lo);
GUS_SetCommand(G_SetLoopEndHi);
outport(GUSDataLo,hi);
}
static void GUS_SetPosition(byte voice, word hi, word lo)
{
GUSSelectVoice(voice);
GUS_SetCommand(G_SetPositionLo);
outport(GUSDataLo,lo);
GUS_SetCommand(G_SetPositionHi);
outport(GUSDataLo,hi);
}
void GUSSetLoopStart(byte voice, longword addr)
/*
Sets loop start of voice.
*/
{
GUS_SetLoopStart(voice,addr << 9, addr >> 7);
}
void GUSSetLoopEnd(byte voice, longword addr)
/*
Sets loop (sample) end of voice.
*/
{
GUS_SetLoopEnd(voice,addr << 9, addr >> 7);
}
void GUSSetPosition(byte voice, longword addr)
/*
Sets position (sample begin) of voice.
*/
{
GUS_SetPosition(voice,addr << 9, addr >> 7);
}
longword GUSReadLoopEnd(byte voice)
/*
Reads loop (sample) end of voice.
*/
{
longword l;
GUSSelectVoice(voice);
GUS_SetCommand(G_ReadLoopEndLo);
l = ((longword)((word)inport(GUSDataLo)) & 0x1FFF) << 7;
GUS_SetCommand(G_ReadLoopEndHi);
l |= ((word)inport(GUSDataLo)) >> 9;
return l;
}
longword GUSReadLoopStart(byte voice)
/*
Reads loop start of voice.
*/
{
longword l;
GUSSelectVoice(voice);
GUS_SetCommand(G_ReadLoopStartLo);
l = ((longword)((word)inport(GUSDataLo)) & 0x1FFF) << 7;
GUS_SetCommand(G_ReadLoopStartHi);
l |= ((word)inport(GUSDataLo)) >> 9;
return l;
}
longword GUSReadPosition(byte voice)
/*
Reads position (sample begin) of voice.
*/
{
longword l;
GUSSelectVoice(voice);
GUS_SetCommand(G_ReadPositionLo);
l = ((longword)((word)inport(GUSDataLo)) & 0x1FFF) << 7;
GUS_SetCommand(G_ReadPositionHi);
l |= ((word)inport(GUSDataLo)) >> 9;
return l;
}
byte GUSReadVoiceMode(byte voice)
/*
Reads mode of voice.
*/
{
GUSSelectVoice(voice);
GUS_SetCommand(G_ReadVoiceMode);
return inportb(GUSDataHi);
}
word GUSReadVoiceFreq(byte voice)
/*
Reads frequency of voice.
*/
{
GUSSelectVoice(voice);
GUS_SetCommand(G_ReadVoiceFreq);
return inport(GUSDataLo) * GUSFreqDivisorTable[GUSVoices];
}
void GUSSetVolume(byte voice, word vol)
/*
Sets volume of voice.
*/
{
GUSSelectVoice(voice);
GUS_SetCommand(G_SetVolume);
outport(GUSDataLo,vol);
}
word GUSReadVolume(byte voice)
/*
Reads volume of voice.
*/
{
GUSSelectVoice(voice);
GUS_SetCommand(G_ReadVolume);
return (word)inport(GUSDataLo);
}
void GUSSetVoiceBalance(byte voice, byte pan)
/*
Sets pan of voice.
*/
{
GUSSelectVoice(voice);
GUS_SetCommand(G_SetVoiceBalance);
outportb(GUSDataHi,pan);
}
byte GUSReadVoiceBalance(byte voice)
/*
Reads pan of voice.
*/
{
GUSSelectVoice(voice);
GUS_SetCommand(G_ReadVoiceBalance);
return inportb(GUSDataHi);
}
/*
void GUSSetDRAM(longword addr, void* buf, longword len)
Set region of GUS DRAM starting at addr, len bytes long to buf.
Not yet optimized for speed.
{
byte* bp;
bp = buf;
while (len-- > 0)
{
GUSPoke(addr++,*bp++);
}
}
*/
void GUSSetDRAM(longword addr, void* buf, longword len)
/*
Set region of GUS DRAM starting at addr, len bytes long to buf.
(Supposedly) partially optimized for speed.
*/
{
byte* bp;
byte addrhi;
word addrlo;
bp = buf;
addrhi = addr >> 16;
addrlo = addr & 0xFFFF;
GUS_SetCommand(G_SetDRAMHi);
outportb(GUSDataHi,addrhi);
GUS_SetCommand(G_SetDRAMLo);
while (len-- > 0)
{
outport(GUSDataLo,addrlo++);
GUS_SetDRAMIO(*bp++);
if (!addrlo)
{
GUS_SetCommand(G_SetDRAMHi);
outportb(GUSDataHi,addrhi);
GUS_SetCommand(G_SetDRAMLo);
}
}
}
/*
void GUSReadDRAM(longword addr, void* buf, longword len)
Read region of GUS DRAM starting at addr, len bytes long to buf.
Not yet optimized for speed.
{
byte* bp;
bp = buf;
while (len-- > 0)
{
*bp++ = GUSPeek(addr++);
}
}
*/
void GUSReadDRAM(longword addr, void* buf, longword len)
/*
Read region of GUS DRAM starting at addr, len bytes long to buf.
(Supposedly) partially optimized for speed.
*/
{
byte* bp;
byte addrhi;
word addrlo;
bp = buf;
addrhi = addr >> 16;
addrlo = addr & 0xFFFF;
GUS_SetCommand(G_SetDRAMHi);
outportb(GUSDataHi,addrhi);
GUS_SetCommand(G_SetDRAMLo);
while (len-- > 0)
{
outport(GUSDataLo,addrlo++);
*bp++ = GUS_ReadDRAMIO();
if (!addrlo)
{
GUS_SetCommand(G_SetDRAMHi);
outportb(GUSDataHi,addrhi);
GUS_SetCommand(G_SetDRAMLo);
}
}
}
void GUSFillDRAM(longword addr, byte b, longword len)
/*
Set region of GUS DRAM starting at addr, len bytes long to b.
(Supposedly) partially optimized for speed.
*/
{
byte addrhi;
word addrlo;
addrhi = addr >> 16;
addrlo = addr & 0xFFFF;
GUS_SetCommand(G_SetDRAMHi);
outportb(GUSDataHi,addrhi);
GUS_SetCommand(G_SetDRAMLo);
while (len-- > 0)
{
outport(GUSDataLo,addrlo++);
GUS_SetDRAMIO(b);
if (!addrlo)
{
GUS_SetCommand(G_SetDRAMHi);
outportb(GUSDataHi,addrhi);
GUS_SetCommand(G_SetDRAMLo);
}
}
}
void GUSSetVolumeRampRate(byte voice, byte incr, byte scale)
/*
Set voice's volume ramp rate. Scale updates as follows:
00 - every access
01 - every 8th access
10 - every 64th access
11 - every 512th access
Advice from Ultradox 2.0: use increments of 8 or less; don't ramp to either
extreme (below 636, above 4032)
*/
{
GUSSelectVoice(voice);
GUS_SetCommand(G_SetVolumeRampRate);
outportb(GUSDataHi,(incr & 0x3f) | ((scale & 3) << 6));
}
byte GUSReadVolumeRampRate(byte voice)
{
GUSSelectVoice(voice);
GUS_SetCommand(G_SetVolumeRampRate);
return inportb(GUSDataHi);
}
void GUSSetVolumeRampStart(byte voice, word vol)
{
GUSSelectVoice(voice);
GUS_SetCommand(G_SetVolumeRampStart);
outportb(GUSDataHi,(vol >> 8));
}
void GUSSetVolumeRampEnd(byte voice, word vol)
{
GUSSelectVoice(voice);
GUS_SetCommand(G_SetVolumeRampEnd);
outportb(GUSDataHi,(vol >> 8));
}
word GUSReadVolumeRampStart(byte voice)
{
GUSSelectVoice(voice);
GUS_SetCommand(G_ReadVolumeRampStart);
return ((word)inportb(GUSDataHi)) << 8;
}
word GUSReadVolumeRampEnd(byte voice)
{
GUSSelectVoice(voice);
GUS_SetCommand(G_ReadVolumeRampEnd);
return ((word)inportb(GUSDataHi)) << 8;
}
void GUSSetVolumeControl(byte voice, byte mode)
{
GUSSelectVoice(voice);
GUS_SetCommand(G_SetVolumeControl);
outportb(GUSDataHi,mode);
}
byte GUSReadVolumeControl(byte voice)
{
GUSSelectVoice(voice);
GUS_SetCommand(G_ReadVolumeControl);
return inportb(GUSDataHi);
}
static int GUSMixerState = 0;
void GUSSetMixer(byte mode)
{
GUSMixerState = mode;
GUS_SetMixer(mode);
}
byte GUSReadMixer()
{
return GUSMixerState;
}
void GUSStopRamp(byte voice)
/*
Stops GUS voice ramp.
*/
{
byte b;
GUSSelectVoice(voice);
b = GUSReadVolumeControl(voice);
if (!(b & V_RampStopped))
{
b |= 3;
GUS_SetCommand(G_SetVolumeControl);
outportb(GUSDataHi,b);
}
}
void GUSStartRamp(byte voice)
/*
Starts GUS voice ramp.
*/
{
byte b;
GUSSelectVoice(voice);
b = GUSReadVolumeControl(voice);
if (b & V_RampStopped)
{
b &= 0xFC;
GUS_SetCommand(G_SetVolumeControl);
outportb(GUSDataHi,b);
}
}
void GUSShutUp()
{
int i;
word vol;
byte incr;
for (i = 0; i < MAXVOICES; i++)
{
GUSSelectVoice(i);
vol = GUSReadVolume(i);
if (vol > 0)
{
GUSSetVolumeRampStart(i,vol);
GUSSetVolumeRampEnd(i,0);
GUSSetVolumeRampRate(i,1,R_Every1);
GUSSetVolumeControl(i,V_StartRamp|V_Decreasing);
GUSStartRamp(i);
/* code for ramps seems not to work; no change in volume */
while ((incr = GUSReadVolumeControl(i) & V_RampStopped)
!= V_RampStopped)
{
vol = GUSReadVolume(i);
if (vol >> 12 == 0) break; /* quiet enough */
printf("vol 0x%04x mode 0x%02x\r",vol,incr);
}
}
}
}